#include "gtest/gtest.h" // Google test
#include "HeteroVector.hpp"
#include <string>
#include <ostream>
namespace TestingCoverage {


class MyClass {
public:
	MyClass(int value) : value_(value) {}

	int getValue() const { return value_; }

private:
	int value_;
};

TEST(HeteroVector, Observers) {
	typedef LOKI_TYPELIST_4(int, char, float, MyClass) MyInputTypes;
	HeteroVector< MyInputTypes > values;

	int arg1[] = { 1, 2 };
	char arg2[] = "amz";
	float arg3[] = { 1.1f, 2.2f };
	MyClass arg4[] = { MyClass(10), MyClass(20), MyClass(30) };
	values.set< 0 >( arg1 );
	values.set< 1 >( arg2 );
	values.set< 2 >( arg3 );
	values.set< 3 >( arg4 );

	ASSERT_EQ(1, values.get< 0 >( 0 ));
	ASSERT_EQ(2, values.get< 0 >( 1 ));
	ASSERT_EQ('a', values.get< 1 >( 0 ));
	ASSERT_EQ('m', values.get< 1 >( 1 ));
	ASSERT_EQ('z', values.get< 1 >( 2 ));
	ASSERT_EQ(1.1f, values.get< 2 >( 0 ));
	ASSERT_EQ(2.2f, values.get< 2 >( 1 ));
	ASSERT_EQ(10, values.get< 3 >( 0 ).getValue() );
	ASSERT_EQ(20, values.get< 3 >( 1 ).getValue() );
	ASSERT_EQ(30, values.get< 3 >( 2 ).getValue() );

	ASSERT_EQ(Length< MyInputTypes >::value, values.numberOfTypes());
	ASSERT_EQ( 2u, values.size< 0 >());
	ASSERT_EQ( 4u, values.size< 1 >());
	ASSERT_EQ( 2u, values.size< 2 >());
	ASSERT_EQ( 3u, values.size< 3 >());

	ASSERT_EQ( 2u, values.getVector< 0 >().size());
}

int factorial(int nThStep) {
	if(nThStep == 0) return 1;
	return factorial(nThStep -1) * nThStep;
}

TEST(HeteroVector, Factorial) {
	// This example shows how input and output values can be paired.
	typedef LOKI_TYPELIST_2(int, int) FactorialTypes;
	const unsigned int input = 0;
	const unsigned int output = 1;

	HeteroVector< FactorialTypes > inputs_outputs;
	int inputs[] = { 0, 1, 2, 3, 4 };
	int outputs[]= { 1, 1, 2, 6, 23 };
	inputs_outputs.set< input >( inputs );
	inputs_outputs.set< output >( outputs );
	inputs_outputs.replace< output >( 4, 24 );

	for(unsigned int nTh = 0; nTh < inputs_outputs.size< input >(); ++nTh)
		ASSERT_EQ(inputs_outputs.get< output >(nTh), factorial( inputs_outputs.get< input >(nTh) ) );
}

string giveMoney(string giver, string receiver, int dollar) {
	ostringstream os;
	os << giver << " gave " << receiver << " " << dollar << " dollars.";
	return os.str();
}

TEST(HeteroVector, GiveMoney) {
	// This example shows how to use more than one input value for different types.
	typedef LOKI_TYPELIST_4(char *, char *, int, char *) GiveMoneyTypes;
	enum { giver, receiver, amount, output };

	HeteroVector< GiveMoneyTypes > inputs_outputs;
	char * givers[] = { "Jay", "RongRong" };
	char * receivers[] = { "Hellen", "Ross" };
	int amounts[] = { 10, 20 };
	inputs_outputs.set< giver >( givers );
	inputs_outputs.set< receiver >( receivers );
	inputs_outputs.set< amount >( amounts );
	inputs_outputs.push_back< output >("Jay gave Hellen 10 dollars.");
	inputs_outputs.push_back< output >("RongRong gave Ross 20 dollars.");

	for(unsigned int nTh = 0; nTh < inputs_outputs.size< giver >(); ++nTh)
		ASSERT_EQ(inputs_outputs.get< output >(nTh),
				giveMoney( inputs_outputs.get< giver >(nTh),
						inputs_outputs.get< receiver >(nTh),
						inputs_outputs.get< amount >(nTh) ) );
}

}
